﻿#include "timer_factory.hh"
#include "../box/memory_allocator.hh"
#include "../box/service_box.hh"
#include "../detail/box_alloc.hh"
#include "../detail/lang_impl.hh"

kratos::service::TimerImpl::TimerImpl(ServiceBox *box) {
  box_ = box;
  timer_wheel_ = make_unique_pool_ptr<util::TimerWheel>(box);
}

kratos::service::TimerImpl::~TimerImpl() {}

/**
 * std::shared_ptr对象删除器
 *
 * \param obj
 */
template <typename T> void ptr_deleter(T *obj) {
  if (!obj) {
    return;
  }
  obj->~T();
  kratos::service::box_free(obj);
}

template <typename T, typename... ArgsT>
auto make_shared(ArgsT... args) -> std::shared_ptr<T> {
  auto pointer = (void *)kratos::service::box_malloc(sizeof(T));
  return std::shared_ptr<T>(new (pointer) T(std::forward<ArgsT>(args)...),
    ptr_deleter<T>);
}

TimerHandle kratos::service::TimerImpl::addTimerOnce(TimerFunc timer_func,
  std::time_t duration, std::uint64_t user_data) {
  auto handle = make_shared<TimerNode_>();
  auto id = internal_id_++;
  handle->user_data = id;
  auto wheel_handle = timer_wheel_->schedule_once(
    [&](kratos::util::TimerID timer_id, std::uint64_t internal_id) -> bool {
      auto it = timer_info_map_.find(internal_id);
      if (it == timer_info_map_.end()) {
        return false;
      }
      if (it->second.func) {
        try {
          it->second.func(it->second.user_data);
        } catch (std::exception& e) {
          if (box_) {
            box_->write_log(
              lang::LangID::LANG_TIMER_HANDLE_EXCEPTION,
              klogger::Logger::EXCEPTION,
              e.what()
            );
          }
          return false;
        }
      }
      return true;
    },
    duration, id);
  auto info = TimerInfo{timer_func, user_data, wheel_handle};
  timer_info_map_.emplace(id, info);
  return handle;
}

TimerHandle kratos::service::TimerImpl::addTimer(TimerFunc timer_func,
  std::time_t duration, std::uint64_t user_data) {
  auto handle = make_shared<TimerNode_>();
  auto id = internal_id_++;
  handle->user_data = id;
  auto wheel_handle = timer_wheel_->schedule(
    [&](kratos::util::TimerID timer_id, std::uint64_t internal_id) -> bool {
      auto it = timer_info_map_.find(internal_id);
      if (it == timer_info_map_.end()) {
        return false;
      }
      if (it->second.func) {
        try {
          it->second.func(it->second.user_data);
        } catch (std::exception& e) {
          if (box_) {
            box_->write_log(
              lang::LangID::LANG_TIMER_HANDLE_EXCEPTION,
              klogger::Logger::EXCEPTION,
              e.what()
            );
          }
          return false;
        }
      }
      return true;
    },
    duration, id);
  auto info = TimerInfo{timer_func, user_data, wheel_handle};
  timer_info_map_.emplace(id, info);
  return handle;
}

void kratos::service::TimerImpl::cancelTimer(TimerHandle handle) {
  if (!handle || handle->deleted) {
    return;
  }
  auto it = timer_info_map_.find(handle->user_data);
  if (it == timer_info_map_.end()) {
    return;
  }
  auto wheel_node = it->second.wheel_handle;
  handle->deleted = true;
  timer_info_map_.erase(it);
  timer_wheel_->cancel(wheel_node);
}

TimerHandle kratos::service::TimerImpl::runOnce(std::time_t now) {
  timer_wheel_->update(now);
  return nullptr;
}

void kratos::service::TimerImpl::clear() {
  for (auto &[k, v] : timer_info_map_) {
    timer_wheel_->cancel(v.wheel_handle);
  }
  timer_info_map_.clear();
}

kratos::service::TimerFactoryImpl::TimerFactoryImpl(ServiceBox *box) {
  box_ = box;
}

kratos::service::TimerFactoryImpl::~TimerFactoryImpl() {}

TimerBase *kratos::service::TimerFactoryImpl::create() {
  return new TimerImpl(box_);
}

void kratos::service::TimerFactoryImpl::destory(TimerBase *timer_base) {
  delete timer_base;
}
